﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CookieSettingStoreBase.cs" company="Radical Research Ltd">
//   Copyright (c) Radical Research Ltd. The MIT License (MIT)
// </copyright>
// <summary>
//   
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace RadicalResearch.MiniState.Cookies
{
    using System;
    using System.Web;

    /// <summary>
    /// Base setting store that custom cookie stores can derive.
    /// </summary>
    public abstract class CookieSettingStoreBase : SettingStoreBase
    {
        /// <summary>
        /// bytes used to salt HMAC validation hash.
        /// </summary>
        private readonly byte[] validationKey;

        #region Constructors and Destructors

        /// <summary>Initializes a new instance of the <see cref="CookieSettingStoreBase"/> class.</summary>
        /// <param name="cookie">The cookie.</param>
        /// <param name="validLifetime">The amount of time that the values remain valid.</param>
        /// <param name="versionNumber">The version number to associate with the values.</param>
        protected CookieSettingStoreBase(
            HttpCookie cookie, TimeSpan validLifetime, short versionNumber)
            : base(new CookieValueCollection(cookie), validLifetime, versionNumber)
        {
            this.validationKey = WebValidation.ValidationKey;
        }

        /// <summary>Initializes a new instance of the <see cref="CookieSettingStoreBase"/> class.</summary>
        /// <param name="cookieName">The cookie name.</param>
        /// <param name="validLifetime">The amount of time that the values remain valid.</param>
        /// <param name="versionNumber">The version number to associate with the values.</param>
        protected CookieSettingStoreBase(
            string cookieName, TimeSpan validLifetime, short versionNumber)
            : this(GetCookie(cookieName), validLifetime, versionNumber)
        {
        }

        /// <summary>Initializes a new instance of the <see cref="CookieSettingStoreBase"/> class.</summary>
        /// <param name="cookieName">The cookie name.</param>
        /// <param name="secure">if set to <c>true</c> the http cookie is marked to only transmit over SSL connections.</param>
        /// <param name="httpOnly">if set to <c>true</c> the http cookie is marked to not be accessible to client side scripts.</param>
        /// <param name="validLifetime">The amount of time that the values remain valid.</param>
        /// <param name="versionNumber">The version number to associate with the values.</param>
        protected CookieSettingStoreBase(
            string cookieName, 
            bool secure, 
            bool httpOnly, 
            TimeSpan validLifetime,
            short versionNumber)
            : this(cookieName, validLifetime, versionNumber)
        {
            this.Cookie.Secure = secure;
            this.Cookie.HttpOnly = httpOnly;
        }

        #endregion

        #region Properties

        /// <summary>
        ///   Gets the cookie.
        /// </summary>
        protected HttpCookie Cookie
        {
            get
            {
                CookieValueCollection cookieValueCollection = (CookieValueCollection)this.Values;
                return cookieValueCollection.Cookie;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>Clears all values in the cookie and sets it to expire.</summary>
        public void Clear()
        {
            this.Cookie.Expires = DateTime.Now.AddYears(-100);
            this.Cookie.Value = string.Empty;
            this.Set();
        }

        /// <summary>
        /// Set the cookie on the current HTTP response.
        /// </summary>
        public void Set()
        {
            HttpContext.Current.Response.Cookies.Set(this.Cookie);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Generates the hash with the machine key configuration.
        /// </summary>
        /// <param name="data">data to hash</param>
        /// <returns>hash bytes</returns>
        protected override byte[] HashData(byte[] data)
        {
            if (null == this.validationKey)
            {
                return base.HashData(data);
            }
            return Validation.HashData(data, this.validationKey);
        }

        /// <summary>Gets the HTTP cookie with the specified name.</summary>
        /// <param name="cookieName">Name of the cookie.</param>
        /// <returns>HTTP cookie from the request if available, otherwise a new cookie with the  specified name.</returns>
        private static HttpCookie GetCookie(string cookieName)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(cookieName);
            if (null == cookie)
            {
                cookie = new HttpCookie(cookieName);
            }

            return cookie;
        }

        #endregion
    }
}